
import {G6GraphEvent, Graph, IEdge, INode, IPoint, ModelConfig} from '@antv/g6';
import ModeType from '../consts/mode-type';
import NodeStateType from '../consts/node-state-type';
import GraphEventName from '../consts/graph-event-name';
import GraphKeyName from '../consts/graph-key-name';
import NodeAttrField from '../consts/node-attr-field';
import {IElement} from '@antv/g-base/src/interfaces';
import StateType from '../consts/state-type';
import G6Util from "../utils/g6-util";


export default function (graph: Graph) {
    graph.on('node:mouseenter', (event: G6GraphEvent) => {
        event.item.toFront();
        // G6Util.showAnchors(event.item.getContainer().getChildren());
        // G6Util.focusAnchor(event.target);
        event.item.setState(NodeStateType.NODE_ANCHOR_VISIBLE, true);
    });
    graph.on('node:mouseleave', (event: G6GraphEvent) => {
        // graph.setItemState(event.item, 'anchor-visible', false);
        // G6Util.hideAnchors(event.item.getContainer().getChildren());
        event.item.setState(NodeStateType.NODE_ANCHOR_VISIBLE, false);
    });
    graph.on('node:mousemove', (event: G6GraphEvent) => {
        if (graph.get(GraphKeyName.DRAG_EDGE_SOURCE)) {
        } else {
            if (G6Util.isAnchor(event)) {
                // G6Util.focusAnchor(event.target);
                event.item.setState(NodeStateType.NODE_ANCHOR_FOCUS, event.target.attr(NodeAttrField.ANCHOR_ANCHOR_INDEX));
            } else {
                // G6Util.showAnchors(event.item.getContainer().getChildren());
                event.item.setState(NodeStateType.NODE_ANCHOR_VISIBLE, true);
            }
        }
    });

    graph.on('node:mousedown', (event: G6GraphEvent) => {
        if (G6Util.isAnchor(event)) {
            const item = event.item as INode;
            item.lock();
        } else {
            const item = event.item as INode;
            item.lock();

            if ((event.originalEvent as MouseEvent).ctrlKey) {
            } else {
                // graph.emit(GraphEventName.NODES_CLEAR_SELECT);
            }
            if (!item.hasState(StateType.SELECTED)) {
                item.setState(StateType.SELECTED, true);
                graph.emit(GraphEventName.ITEM_SELECTED,  item);
            }
        }
    });
    graph.on('node:mouseup', (event: G6GraphEvent) => {
        graph.getNodes().forEach(node => {
            node.lock();
        });

        if (G6Util.isAnchor(event)) {
        } else {
            const item = event.item as INode;
            if ((event.originalEvent as MouseEvent).ctrlKey) {
            } else {
                graph.emit(GraphEventName.ITEMS_CLEAR_SELECT);
            }
            if (!item.hasState(StateType.SELECTED)) {
                item.setState(StateType.SELECTED, true);
                graph.emit(GraphEventName.ITEM_SELECTED,  item);
            }
        }
    });

    graph.on('node:dragstart', (event: G6GraphEvent) => {
        graph.set(GraphKeyName.DRAG_NODE_IDS, null);
        if (G6Util.isAnchor(event)) {
            const targetAnchor = event.target;
            // G6Util.highlightOtherNodesAnchors(graph.getNodes().filter(node => node !== event.item));
            // graph.emit(GraphEventName.NODES_ANCHOR_BOLD, event.item);
            graph.getNodes().filter(item => item !== event.item).forEach(item => item.setState(NodeStateType.NODE_ANCHOR_BOLD, true))

            graph.set(GraphKeyName.MODE, ModeType.EDGE_ADD);
            graph.set(GraphKeyName.DRAG_EDGE_SOURCE, {
                node: event.item,
                anchor: targetAnchor
            });
            graph.set(GraphKeyName.DRAG_EDGE_CANDIDATE_TARGET, null);
            graph.set(GraphKeyName.DRAG_EDGE_TARGET, null);
        } else {
            graph.set(GraphKeyName.MODE, ModeType.NODE_MOVE);
            const item = event.item as INode;

            if (!(event.originalEvent as MouseEvent).ctrlKey) {
                graph.getNodes().filter((node: INode) => node !== event.item).forEach((node: INode) => node.setState(StateType.SELECTED, false));
                graph.getEdges().forEach((edge: IEdge) => edge.setState(StateType.SELECTED, false));
            }
            const selectedNodes = graph.findAll('node', (node: INode) => node.hasState(StateType.SELECTED));
            // graph.set('$dragNodeId', [event.item.getModel().id]);
            if (selectedNodes) {
                graph.set(GraphKeyName.DRAG_NODE_IDS, selectedNodes.map((node: INode) => node.getModel().id));
            }

            item.setState(StateType.SELECTED, true);
            graph.emit(GraphEventName.ITEM_SELECTED,  item);
            const { clientX, clientY } = event;
            graph.set(GraphKeyName.DRAG_NODE_START_POINT, { x: clientX, y: clientY });
        }
    });
    graph.on('node:drag', (event: G6GraphEvent) => {
        // console.log('node:drag');
        const mode = graph.get(GraphKeyName.MODE);
        if (mode === ModeType.NODE_ADD || mode === ModeType.NODE_MOVE) {
            graph?.emit(GraphEventName.GHOST_NODES_DISPLAY, event);
        } else if (mode === ModeType.EDGE_ADD) {
            graph.set(GraphKeyName.DRAG_EDGE_CANDIDATE_ANCHOR, null);
            graph?.emit(GraphEventName.GHOST_EDGE_DISPLAY, event);

            const edgeSource = graph.get(GraphKeyName.DRAG_EDGE_SOURCE);

            const candidateNode = graph.get(GraphKeyName.DRAG_EDGE_CANDIDATE_TARGET);
            if (!candidateNode) {
                return;
            }
            // graph.getNodes().forEach((node) => {
            //     node.getContainer().getChildren()
            //         .filter((child: IElement) => child.attr(NodeAttrField.ANCHOR_IS_ANCHOR))
            //         .forEach((child: IElement) => child.attr({...anchorStyle.boldStyle}));
            // });
            graph.getNodes()
                .filter(node => node !== edgeSource?.node)
                .forEach((node) => {
                    node.setState(NodeStateType.NODE_ANCHOR_BOLD, true);
                });

            // if (x >= minX && x <= maxX && y >= minY && y <= maxY) {
            if (G6Util.inNodeArea(candidateNode, event)) {
                const anchor = G6Util.getNearestAnchor(candidateNode, event) as IPoint;
                // candidateNode.getContainer().getChildren()
                //     .filter((child: IElement) => child.attr(NodeAttrField.ANCHOR_IS_ANCHOR))
                //     .forEach((child: IElement) => child.attr({...anchorStyle.boldStyle}));
                const anchorElement = candidateNode.getContainer().getChildren().find((child: IElement) => child.attr(NodeAttrField.ANCHOR_ANCHOR_INDEX) === anchor.anchorIndex);
                // anchorElement?.attr(anchorStyle.nearStyle);
                candidateNode.setState(NodeStateType.NODE_ANCHOR_NEAR, anchor.anchorIndex);
                graph.set(GraphKeyName.DRAG_EDGE_CANDIDATE_ANCHOR, anchor);
                graph.set(GraphKeyName.DRAG_EDGE_TARGET, {
                    node: candidateNode,
                    anchor: anchorElement,
                });
            } else {
                // candidateNode.getContainer().getChildren()
                //     .filter((child: IElement) => child.attr(NodeAttrField.ANCHOR_IS_ANCHOR))
                //     .forEach((child: IElement) => child.attr({...anchorStyle.boldStyle}));
                graph.getNodes()
                    .filter(node => node !== edgeSource?.node)
                    .forEach((node) => {
                        node.setState(NodeStateType.NODE_ANCHOR_BOLD, true);
                    });

            }

        }
    });
    graph.on('node:dragend', (event: G6GraphEvent) => {
        // console.log('node:dragend');
        graph.getNodes().forEach(node => {
            node.unlock();
        });

        // graph.emit(GraphEventName.NODES_ANCHOR_THIN);
        graph.getNodes().forEach(item => item.setState(NodeStateType.NODE_ANCHOR_BOLD, false));

        const mode = graph.get(GraphKeyName.MODE);
        const edgeSource = graph.get(GraphKeyName.DRAG_EDGE_SOURCE);
        const edgeTarget = graph.get(GraphKeyName.DRAG_EDGE_TARGET);
        if (mode === ModeType.EDGE_ADD && edgeSource && edgeTarget) {
            const targetAnchor = event.target;
            graph.set(GraphKeyName.DRAG_EDGE_TARGET, {
                node: event.item,
                anchor: targetAnchor
            });
            // graph.emit(GraphEventName.NODES_ANCHOR_THIN);
            graph.getNodes().forEach(item => item.setState(NodeStateType.NODE_ANCHOR_BOLD, false));

            const confictedEdges = graph.findAll('edge', (edge: IEdge) => {
                const {source, target} = edge.getModel() || {};
                return source === edgeSource.node.getModel().id && target === edgeTarget.node.getModel().id;
            });
            if (confictedEdges && confictedEdges.length) {
                graph.removeItem(confictedEdges[0] as IEdge);
            }

            const model = {
                id: G6Util.generateNodeId('direct-polyline', 6),
                type: 'direct-polyline',
                // type: 'polyline',
                source: edgeSource.node.getModel().id,
                target: edgeTarget.node.getModel().id,
                sourceAnchor: edgeSource.anchor.attr('$anchorIndex'),
                targetAnchor: edgeTarget.anchor.attr('$anchorIndex'),
                style: {
                    // offset: 100,
                    radius: 5,
                    stroke: '#aeaeae',
                }
            } as ModelConfig;

            graph.addItem('edge', model, true);
            graph.removeItem('ghost-edge');
            graph.set(GraphKeyName.MODE, null);
            graph.set(GraphKeyName.DRAG_EDGE_SOURCE, null);
            graph.set(GraphKeyName.DRAG_EDGE_TARGET, null);
            graph.set(GraphKeyName.DRAG_EDGE_CANDIDATE_TARGET, null);
            graph.set(GraphKeyName.DRAG_EDGE_CANDIDATE_ANCHOR, null);
        } else {
            graph.removeItem('ghost-edge');
            graph.emit(GraphEventName.NODE_DROP, event);
            graph.set(GraphKeyName.MODE, '');
            setTimeout(() => {
                G6Util.removeDragGhostNodes(graph);
                graph.set(GraphKeyName.DRAG_NODE_IDS, null);
            }, 0);
        }
    });

    graph.on('node:dragenter', (event: G6GraphEvent) => {
        // console.log('node:dragenter');
        event.item.toFront();
        const mode = graph.get(GraphKeyName.MODE);
        if (mode === ModeType.EDGE_ADD) {
            const edgeSource = graph.get(GraphKeyName.DRAG_EDGE_SOURCE);
            if (event.item !== edgeSource.node) {
                graph.set(GraphKeyName.DRAG_EDGE_CANDIDATE_TARGET, event.item);
            } else {
                graph.set(GraphKeyName.DRAG_EDGE_CANDIDATE_TARGET, null);
            }
        }
    });

    graph.on('node:drop', (event: G6GraphEvent) => {
        // console.log('node:drop');

        const mode = graph.get(GraphKeyName.MODE);
        const edgeSource = graph.get(GraphKeyName.DRAG_EDGE_SOURCE);
        const edgeTarget = graph.get(GraphKeyName.DRAG_EDGE_TARGET);
        if (mode === ModeType.EDGE_ADD && edgeSource && edgeTarget) {
            // const targetAnchor = event.target;
            // graph.set(GraphKeyName.DRAG_EDGE_TARGET, {
            //     node: event.item,
            //     anchor: targetAnchor
            // });
            // graph.emit(GraphEventName.NODES_ANCHOR_THIN);
            //
            // console.log(' draw line: ', edgeSource, edgeTarget);
            //
            // const model = {
            //     id: G6Util.generateNodeId('direct-polyline', 6),
            //     // type: 'direct-polyline',
            //     type: 'polyline',
            //     label: 'direct-polyline',
            //     source: edgeSource.node.getModel().id,
            //     target: edgeTarget.node.getModel().id,
            //     sourceAnchor: edgeSource.anchor.attr('$anchorIndex'),
            //     targetAnchor: edgeTarget.anchor.attr('$anchorIndex'),
            // } as ModelConfig;
            //
            // graph.addItem('edge', model);
        }
    });
}
